2 - funksjoner
Funksjoner er helt sentralt i all slags programmering. Et program uten egendefinerte funksjoner er bare en liste med kommandoer, og det er svært begrenset hva du kan få til med det. Heldigvis er det ekstremt enkelt å lage egne funksjoner i Python.
En vanlig nybegynnerfeil i programmering er å ikke utnytte muligheten til å lage egne funksjoner tilstrekkelig. Hovedregelen er at om det er noe av koden din som repeteres, så skal du lage en ny funksjon som håndterer dette. Målet er at antall linjer i hver funksjon skal være så lite som mulig. Men før vi lærer å lage egne funksjoner, skal vi se litt på hvordan funksjoner brukes.
Python kommer med en rekke innebygde funksjoner. I tillegg kan du importere pakker som har en lang rekke funksjoner. Her er noen innebygde funksjoner i Python:
print( max(2.4,2.0) )
print( min(2.4, 2.0) )
print( abs(-2.4) )
print( len("abcde") )
Legg merke til at funksjonene max
, min
, abs
og len
alle er omsluttet av funksjonen print
som sørger for at resultatet skrives ut nedenfor. En funksjon kan altså returnere noe, slik som max
, min
, abs
og len
, eller gjøre noe slik som print
, eller begge deler.
Funksjonen len
angir antall bokstaver, men kan også angi lengde på en liste. En liste i python skriver du som [1,'hei',a]
. En liste kan altså bestå av flere ulike typer variabler og konstanter. De to første elementene her er konstanter, og den tredje er en variabel som er satt lik et flyttal i eksemplet under.
a=50.8
print([1,'hei',a])
len([1,'hei',a])
I Python er det svært enkelt å importere ferdig pakker for som kan kjøre ferdige funksjoner. Vi skal her se på et eksempel med noe som heter eksponentialfunksjonen. Denne finnes blant annet i pakken numpy
. Dette er en svært nyttig pakke for matematiske beregninger. Se seksjonen om "Installere Python-pakker" i "0 - installasjon og tips.ipynb" for å installere Numpy.
Som vi skal se lenger ned, så er eksponentialfunksjonen viktig innen økonomifaget fordi den brukes i vekstberegninger, for eksempel for befolkning eller produksjon.
Som nevnt tidligere så er potensen $y^x$ et grunntall $y$ opphøyet i en eksponent $x$. Når vi snakker om eksponenten i bestemt form, så betyr det at grunntallet er et bestemt tall. Dette tallet kalles $e$, etter matematikeren Euler, og er tilnærmet $e\approx2.71828$ (det er egentlig en uendelig rekke med siffer etter komma)
Siden Python ikke har noen innebygd funksjon for eksponenten, importerer vi altså først numpy
-pakken, som har en slik funksjon. For å få mindre skriving senere, er det vanlig å importere numpy
som aliaset np
. Dette gjør vi slik:
import numpy as np
Navnet på eksponent-funksjonen i numpy er exp. Skal vi for eksempel finne hva $e^2=2.71828^2$ er, skriver vi
np.exp(2)
Om vi ønsker å vite hva eksponenten er med litt mer enn fem desimaler, kan vi finne eksponenten av 1, siden ethvert tall opphøyd i 1 er seg selv:
np.exp(1)
Eksponenten til 5 er for eksempel 2.71828 opphøyet i 5:
np.exp(1)**5
np.exp(5)
For å opphøye noe bruker vi altså dobbelt gangetegn **
.
Logartimen er det eksakt motsatte av eksponentialfunksjonen. Tar vi logaritmen av eksponentialfunksjon, får vi argumentet til sistnevnte. Tar vi eksponentialfunksjonen av logartimen får vi også argumentet til logartimen. I en av oppgavene under skal du vise dette.
Vi skal her se hvordan vi kan lage egne funksjoner for renteregning.
La oss starte med å lage en funksjon for et lån uten avdrag. Om banken legger til renten én gang i året og renten er r, så er lånet på x om T år gitt som $x\cdot (1+r)^{T}$. Dette kan vi programmere som følgende funksjon:
def account_balance(x,r,T):
return x*(1+r)**T
Nøkkelordet return
sørger for at det som står etter return
returneres av funksjonen. Dersom funksjonen ikke har return
i seg, returneres None
, som representerer "ingenting" i Python.
Om renten på lånet er 20%=0.2 og innskuddet er 100, så er beløpet om to år dermed:
account_balance(100,0.2,2)
La oss nå tenke oss at banken legger til renter n ganger i året. Om for eksempel n=12, så legger banken til renter måndedlig. Vi kan da regne ut lånet ved ulik antall forrentninger per år. Formelen for dette er $x\cdot (1+\frac{r}{n})^{T\cdot n}$.
Formelen har en enkel og intuitiv forklaring: Vi deler rentesatsen på antall perioder, samtidig som renten legges til i hver periode. Renten i hver periode er dermed $\frac{r}{n}$, og dette forrentes totalt $T\cdot n$ ganger. Dette kan programmeres i pyton som
def account_balance_n(x,r,T,n):
return x*(1+r/n)**(T*n)
La oss nå sammenligne å legge til renten hver måned, med å legge den til årlig:
print(account_balance_n(100,0.2,2,12))
print(account_balance_n(100,0.2,2,1))
Vi ser at det lønner seg en god del å legge til renten hver måned. La oss nå anta at rentene legges til hvert sekund i året. Det er 31 536 000 sekunder i et år. I så fall er innskuddet om to år verdt:
account_balance_n(100,0.2,2,31536000)
Banken vil altså tjene litt på å gjøre det. I stedet for å regne ut med daglig forrentning kan vi bruke kontinuerlig, som vil si at vi bruker eksponentialfunksjonen
def account_balance_exponential(x,r,T):
return x*np.exp(r*T)
Dette gir oss verdien på lånet når renten legges til "kontinuerlig", altså oftere enn hvert sekund. La oss nå regne ut verdien av gjelden om to år med kontinuerlig forrentning:
account_balance_exponential(100,0.2,2)
Vi ser at dette er omtrent identisk med når vi la til renten hvert sekund. Eksponentialfunksjonen er altså en måte å regne ut vekst når økningen legges til veldig hyppig. Forrentning hvert sekund og kontinuerlig forrentig gir altså omtrent det samme resultatet. "Kontinuerlig forrentning" er et abstrakt begrep, men det betyr i realiteten bare at vi legger til veksten "veldig hyppig".
Årsaken til at vi ofte bruker logaritmen og eksponentialfunksjonen istedet for å legge til veksten hvert sekund, ar et det er veldig mye enklere å regne med logartimer enn vanlig prosentregning.
def f(x):
return 2*x
La oss nå sjekke følgende:
print(f(8)) # printer resultatet av funksjonen når argumentet er 8
print(type(f)) # printer hvilken type objekt det er
print(type(f(5))) # printer hvilken type resultate av funksjonen er
Funksjonen dobler argumentet, så med 8 som argument blir resultatet 16. Symbolet f
representerer nå en funksjon (function
), som vi ser er resultatet av print(type(f))
. Men dersom vi kaller funksjonen med for eksemple argumentet 5, får vi resultatet av funksjonen, som er av type int
. Funksjoner trenger imidlertid ikke retunere noe. Dette er også en funksjon:
def hello():
print('Hello world!')
hello()
Vi ser at denne funksjonen ikke har return
-nøkkelordet. Dermed returneres ingen verdi. I stedet for å returnere noe, så gjør denne funksjonen noe, nemlig å skrive ut 'Hello world"' til skjermen.
Du kaller funksjoner ved å skrive funksjonen med argumentet i parentes etter navnet. Som vi ser her, når det ikke er noen argumenter, skriver du tomme parenteser.
En funksjon er også en variabel og et objekt. Du kan for eksempel definere en ny variabel som en tidligere definert funksjon:
hi=hello
hi()
Av og til ønsker du at argumentene til en funksjon skal være valgfrie. Da kan du definere en standard ("default") verdi for argumentet:
def f(x,a=1.02e+2):
return a*x
print(f"Innehaveren krevde i utgangspunkltet {f(5)} kroner for fem epler.\n"
f"Det var alt for mye, så jeg tilbydde {f(5,1.02e-2)} kroner.\n"
f"Vi ble til slutt enige om {f(5,1.02e+1)} kroner.\n")
Legg merke til at argumentene til en funksjon settes inn i en parentes. For f(x,y)
for eksempel, så er x
og y
argumenter til funksjonen f
. Disse står inne i en parentes (x,y)
. Denne parentesen er et objekt i seg selv og kalles en tuple
. Det går derfor an å definere alle argumentene først, og så la funksjonen evaluere dem. La oss for eksempel si at x=20
og y=50
, slik at (x,y)=(20,50)
. Vi kan da definere variablene først og så evaluere funksjonen på følgende måte
args=(20,50)
f(*args)
Dette er en mulighet som kan være veldig nyttig i mange sammenhenger. En tuple
skiller seg fra en liste (list
) ved at sistnevnte defineres med hakeparenteser []
i stedet for runde parenteser ()
. Vi skal senere komme tilbake til flere forskjeller på en liste og en tuple.
La oss definere en tilbud- og etterspørselsfunksjon. Disse funksjonene skal vise hvilken pris konsumentene maksimalt er villige til å betale for en gitt mengde, og hvor mye produsentene minimum må ha for å selge varen.
Den høyeste prisen er det bare noen få konsumenter som er villige til å betale, så desto større kvantum x
som skal selges, desto lavere må prisen være. Etterspørselsfunksjonen må derfor være fallende (lavere pris ved høyere kvantum).
Her er et forslag til etterspørselsfunksjon:
#Creating a function that returns total demand given a price p
def demand(x):
return 125/(5+x)
Den laveste prisen er det bare den mest effektive bedriften som klarer å produsere lønnsomt for. Ettersom kvantum x
øker vil flere, men mindre effektive bedrifter, tilby varen. Desto større kvantum, desto høyere pris kreves per enhet. Tilbudsfunksjonen bør derfor være stigende (høyere pris ved høyere kvantum).
Her er forslag til tilbudsfunksjoner:
#Creating a function that returns total supply given a price p
def supply(x):
return x**2
Sjekk med nummeriske eksempler at tilbudskurven faktisk er økende og etterspørselskurven faktisk er fallende.
Svar her
Svar her
Finn med prøving og feiling eller på annen måte hvilket kvantum som gir likhet mellom tilbud og etterspørsel. Hvilken pris gir slik kvantum?
Svar her
time_period
, og en pris per time angitt av argumentet p
. Funksjonen skal forvente at time_period er på formatet "hh:mm:ss" (en streng). Du kan trekke ut timer, minutter og sekunder fra en tekstreng på det foreslåtte formatet, ved å bruke koden hh,mm,ss=int(time_period[0:2]),int(time_period[3:5]),int(time_period[6:])
. Du må konvertere tiden til timer på desimalform for å regne ut kostnad. Det kan du gjøre med denne formelen: tt_dec=hh+mm/60+ss/(60*60)
.
adj_list
. Funksjonen skal returnere et tilfeldig element fra listen. Du kan for eksempel teste funksjonen med denne listen ['dyr', 'billig', 'pareto optimal', 'høy', 'lav', 'ineffektiv']
. For å plukke et tilfeldig element fra en liste adj_list
med 4
elementer, skriver du adj_list[np.random.randint(4)]
.
a) Bruk eksponentialfunksjonen og log-funksjonen i numpy til å vise at log-funksjonen er det motsatte av eksponentialfunksjonen.
b) Regn ut hvor mange prosent 205,210,220,230 og 240 er av 200. Regn også ut logaritmen av 205/200, 210/200 ... osv. og kommenter resultatet.
Lag en funksjon som returnerer det høyeste av funksjonene account_balance_exponential
og account_balance
. Er dette en nyttig funksjon?
a) Bruk innebygde Python-funksjoner til å finne maksimum, minimum, absoluttverdi og lengden av teksten "1, -5, 10". Bruk funksjonen eval
til å konvertere verdiene til en tallrekke
b) Nåverdien til et beløp er det en trenger å sette i banken i dag for å få beløpet i fremtiden. Formelen for dette er X/((1+r)**T)
. Lag en funksjon som retuner nåverdien av et beløp. Lag også en funksjon som nåverdien er med kontinuerlig forrentning.
c) Lag en funkson som returnerer nåverdi eller sluttverdi avhengig av hva argumentene er. Sluttverdi er det som er beregnet i avsnittet "Lage egne funksjoner - renteregning". Funksjonen skal ha antall forrentninger per år som argument, og skal returnere kontinuerlig forrentning dersom antall forrentninger er høyere enn 100
Du kan bruke en if
- betingelse til å velge hva en funksjon skal gjøre slik:
def function(return_one):
if return_one:
return 1
else:
return 2
print(function(True))
print(function(False))